/**
 * iWeb2GooglePages - Converts export iWeb content and uploads it to GPs.
 * Copyright (C) 2008 Will Droste
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. 
 */
package org.iweb2gps;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.xml.sax.SAXException;

public class IWebConverter {

    public interface Listener {
        /**
         * Fires for each file converted.
         * 
         * @param idx
         *            convert progress of the file.
         * @param total
         *            sum of all files that will be processed.
         * @param f
         *            currently converting file.
         */
        void processing(int idx, int total, File f);
    }

    private final File src;
    private final File dest;
    private int total;
    private int count;

    private final List<Listener> listeners;

    /**
     * Convert the export of the iWeb directory to a single directory.
     */
    public IWebConverter(File src, File dest) {
        this.src = src;
        this.dest = dest;
        this.listeners = new ArrayList<Listener>();
    }

    /**
     * Adds a listener.
     */
    public void addListener(Listener listener) {
        if (listener != null) {
            this.listeners.add(listener);
        }
    }

    /**
     * Copies files from the source to the destination by flatting the names.
     */
    public void convert() throws Exception {
        // start the count..
        this.count = 0;
        // find the total..
        this.total = IOUtil.recursiveCount(src);
        // start the recursive copy that flattens files..
        recursiveCopy(src);
    }

    /**
     * Convert the source file to a destination file.
     */
    File convertFilename(File f) {
        // remove the source path from the file..
        String sub = f.toString().substring(src.toString().length() + 1);
        // convert the directory handles to '_'..
        sub = sub.replace('/', '_').replace(' ', '_');
        // create new file
        File ret = new File(dest, sub);
        return ret;
    }

    /**
     * Process files recursively.
     */
    void recursiveCopy(File f) throws Exception {
        // filter out MAC files
        if (".DS_Store".equals(f.getName()) || "__MACOSX".equals(f.getName())) {
            return;
        }
        // recursively look through the directories..
        if (f.isDirectory()) {
            for (File fn : f.listFiles()) {
                recursiveCopy(fn);
            }
        } else {
            // converts and copies file..
            this.count++;
            // tell all listeners..
            for (Listener listener : listeners) {
                listener.processing(count, total, f);
            }
            // process file..
            convertFile(f);
        }
    }

    /**
     * Converts and copies the file based on the suffix.
     * 
     * @throws SAXException
     */
    void convertFile(File f) throws IOException {
        // overwrite the files..
        File dst = convertFilename(f);
        // process the various types of files..
        String suffix = getFileSuffix(f);
        if ("JS".equalsIgnoreCase(suffix) || 
                "CSS".equalsIgnoreCase(suffix) || 
                "HTML".equalsIgnoreCase(suffix)) {
            String fileString = readFile(f);
            if ("JS".equalsIgnoreCase(suffix)) {
                fileString = fileString.replaceAll("_files\\/", "_files_");
            } else if ("CSS".equalsIgnoreCase(suffix)) {
                // convert the relative urls..
                fileString = fileString.replaceAll("\\.\\.\\/Images\\/", "Images_");
            } else if ("HTML".equalsIgnoreCase(suffix)) {
                final String prefix = f.getName().replace(".html", "_files").replace("+", "\\+");
                final String find = prefix + '/';
                final String replace = prefix + '_';
                fileString = replace(fileString, find, replace);
            }
            // shared instances
            fileString = fileString.replaceAll("Scripts\\/", "Scripts_");
            fileString = fileString.replaceAll("Widgets\\/", "Widgets_");
            fileString = fileString.replaceAll("Navbar\\/", "Navbar_");
            fileString = fileString.replaceAll("SharedResources\\/", "SharedResources_");
            // write the string back out as a file..
            writeFile(fileString, dst);
        } else {
            // copy the file to the destination just content..
            IOUtil.copyFile(f, dst);
        }
    }

    // =======================================================================
    // Static helper methods
    // =======================================================================

    static String readFile(File f) throws IOException {
        FileReader frdr = new FileReader(f);
        String ret = IOUtil.readerToString(frdr);
        frdr.close();
        return ret;
    }

    static void writeFile(String input, File dest) throws IOException {
        FileWriter fwrt = new FileWriter(dest);
        fwrt.write(input);
        fwrt.flush();
        fwrt.close();
    }

    static String replaceSlash(String pattern, String input) {
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(input);
        StringBuffer sb = new StringBuffer();
        while (m.find()) {
            String replace = m.group().replace('/', '_').replace("%20", "_");
            m.appendReplacement(sb, replace);
        }
        m.appendTail(sb);
        return sb.toString();
    }

    /**
     * Returns the suffix of a file.
     */
    static String getFileSuffix(File f) {
        String ret = null;
        if (f != null) {
            String fn = f.getName();
            int idx = fn.lastIndexOf('.');
            if (idx != -1) {
                ret = fn.substring(idx + 1);
            }
        }
        return ret;
    }

    static String replace(String text, String searchString, String replacement) {
        return replace(text, searchString, replacement, -1);
    }

    static String replace(String text, String searchString, String replacement, int max) {
        if (replacement == null || max == 0) {
            return text;
        }
        int start = 0;
        int end = text.indexOf(searchString, start);
        if (end == -1) {
            return text;
        }
        int replLength = searchString.length();
        int increase = replacement.length() - replLength;
        increase = (increase < 0 ? 0 : increase);
        increase *= (max < 0 ? 16 : (max > 64 ? 64 : max));
        StringBuilder buf = new StringBuilder(text.length() + increase);
        while (end != -1) {
            buf.append(text.substring(start, end)).append(replacement);
            start = end + replLength;
            if (--max == 0) {
                break;
            }
            end = text.indexOf(searchString, start);
        }
        buf.append(text.substring(start));
        return buf.toString();
    }

}
